home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / tcl / tcl70b2.lha / tcl7.0b2 / tclCkalloc.c < prev    next >
C/C++ Source or Header  |  1993-07-15  |  17KB  |  586 lines

  1. /* 
  2.  * tclCkalloc.c --
  3.  *    Interface to malloc and free that provides support for debugging problems
  4.  *    involving overwritten, double freeing memory and loss of memory.
  5.  *
  6.  * Copyright (c) 1991-1993 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Permission is hereby granted, without written agreement and without
  10.  * license or royalty fees, to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose, provided that the
  12.  * above copyright notice and the following two paragraphs appear in
  13.  * all copies of this software.
  14.  * 
  15.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  16.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  17.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  18.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  19.  *
  20.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  21.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  22.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  23.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  24.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  25.  *
  26.  * This code contributed by Karl Lehenbauer and Mark Diekhans
  27.  *
  28.  */
  29.  
  30. #include "tclInt.h"
  31.  
  32. #define FALSE    0
  33. #define TRUE    1
  34.  
  35. #ifdef TCL_MEM_DEBUG
  36. #ifndef TCL_GENERIC_ONLY
  37. #include "tclUnix.h"
  38. #endif
  39.  
  40. #define GUARD_SIZE 8
  41.  
  42. struct mem_header {
  43.         long               length;
  44.         char              *file;
  45.         int                line;
  46.         struct mem_header *flink;
  47.         struct mem_header *blink;
  48.         unsigned char      low_guard[GUARD_SIZE];
  49.         char               body[1];
  50. };
  51.  
  52. static struct mem_header *allocHead = NULL;  /* List of allocated structures */
  53.  
  54. #define GUARD_VALUE  0341
  55.  
  56. /* static char high_guard[] = {0x89, 0xab, 0xcd, 0xef}; */
  57.  
  58. static int total_mallocs = 0;
  59. static int total_frees = 0;
  60. static int current_bytes_malloced = 0;
  61. static int maximum_bytes_malloced = 0;
  62. static int current_malloc_packets = 0;
  63. static int maximum_malloc_packets = 0;
  64. static int break_on_malloc = 0;
  65. static int trace_on_at_malloc = 0;
  66. static int  alloc_tracing = FALSE;
  67. static int  init_malloced_bodies = FALSE;
  68. #ifdef MEM_VALIDATE
  69.     static int  validate_memory = TRUE;
  70. #else
  71.     static int  validate_memory = FALSE;
  72. #endif
  73.  
  74.  
  75. /*
  76.  *----------------------------------------------------------------------
  77.  *
  78.  * dump_memory_info --
  79.  *     Display the global memory management statistics.
  80.  *
  81.  *----------------------------------------------------------------------
  82.  */
  83. static void
  84. dump_memory_info(outFile) 
  85.     FILE *outFile;
  86. {
  87.         fprintf(outFile,"total mallocs             %10d\n", 
  88.                 total_mallocs);
  89.         fprintf(outFile,"total frees               %10d\n", 
  90.                 total_frees);
  91.         fprintf(outFile,"current packets allocated %10d\n", 
  92.                 current_malloc_packets);
  93.         fprintf(outFile,"current bytes allocated   %10d\n", 
  94.                 current_bytes_malloced);
  95.         fprintf(outFile,"maximum packets allocated %10d\n", 
  96.                 maximum_malloc_packets);
  97.         fprintf(outFile,"maximum bytes allocated   %10d\n", 
  98.                 maximum_bytes_malloced);
  99. }
  100.  
  101. /*
  102.  *----------------------------------------------------------------------
  103.  *
  104.  * ValidateMemory --
  105.  *     Procedure to validate allocted memory guard zones.
  106.  *
  107.  *----------------------------------------------------------------------
  108.  */
  109. static void
  110. ValidateMemory (memHeaderP, file, line, nukeGuards)
  111.     struct mem_header *memHeaderP;
  112.     char              *file;
  113.     int                line;
  114.     int                nukeGuards;
  115. {
  116.     unsigned char *hiPtr;
  117.     int   idx;
  118.     int   guard_failed = FALSE;
  119.     int byte;
  120.     
  121.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  122.         byte = *(memHeaderP->low_guard + idx);
  123.         if (byte != GUARD_VALUE) {
  124.             guard_failed = TRUE;
  125.             fflush (stdout);
  126.         byte &= 0xff;
  127.             fprintf(stderr, "low guard byte %d is 0x%x  \t%c\n", idx, byte,
  128.                 (isprint(byte) ? byte : ' '));
  129.         }
  130.     }
  131.     if (guard_failed) {
  132.         dump_memory_info (stderr);
  133.         fprintf (stderr, "low guard failed at %lx, %s %d\n",
  134.                  memHeaderP->body, file, line);
  135.         fflush (stderr);  /* In case name pointer is bad. */
  136.         fprintf (stderr, "%d bytes allocated at (%s %d)\n", memHeaderP->length,
  137.         memHeaderP->file, memHeaderP->line);
  138.         panic ("Memory validation failure");
  139.     }
  140.  
  141.     hiPtr = (unsigned char *)memHeaderP->body + memHeaderP->length;
  142.     for (idx = 0; idx < GUARD_SIZE; idx++) {
  143.         byte = *(hiPtr + idx);
  144.         if (byte != GUARD_VALUE) {
  145.             guard_failed = TRUE;
  146.             fflush (stdout);
  147.         byte &= 0xff;
  148.             fprintf(stderr, "hi guard byte %d is 0x%x  \t%c\n", idx, byte,
  149.                 (isprint(byte) ? byte : ' '));
  150.         }
  151.     }
  152.  
  153.     if (guard_failed) {
  154.         dump_memory_info (stderr);
  155.         fprintf (stderr, "high guard failed at %lx, %s %d\n",
  156.                  memHeaderP->body, file, line);
  157.         fflush (stderr);  /* In case name pointer is bad. */
  158.         fprintf (stderr, "%d bytes allocated at (%s %d)\n", memHeaderP->length,
  159.         memHeaderP->file, memHeaderP->line);
  160.         panic ("Memory validation failure");
  161.     }
  162.  
  163.     if (nukeGuards) {
  164.         memset ((char *) memHeaderP->low_guard, 0, GUARD_SIZE); 
  165.         memset ((char *) hiPtr, 0, GUARD_SIZE); 
  166.     }
  167.  
  168. }
  169.  
  170. /*
  171.  *----------------------------------------------------------------------
  172.  *
  173.  * Tcl_ValidateAllMemory --
  174.  *     Validates guard regions for all allocated memory.
  175.  *
  176.  *----------------------------------------------------------------------
  177.  */
  178. void
  179. Tcl_ValidateAllMemory (file, line)
  180.     char  *file;
  181.     int    line;
  182. {
  183.     struct mem_header *memScanP;
  184.  
  185.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink)
  186.         ValidateMemory (memScanP, file, line, FALSE);
  187.  
  188. }
  189.  
  190. /*
  191.  *----------------------------------------------------------------------
  192.  *
  193.  * Tcl_DumpActiveMemory --
  194.  *     Displays all allocated memory to stderr.
  195.  *
  196.  * Results:
  197.  *     Return TCL_ERROR if an error accessing the file occures, `errno' 
  198.  *     will have the file error number left in it.
  199.  *----------------------------------------------------------------------
  200.  */
  201. int
  202. Tcl_DumpActiveMemory (fileName)
  203.     char *fileName;
  204. {
  205.     FILE              *fileP;
  206.     struct mem_header *memScanP;
  207.     char              *address;
  208.  
  209.     fileP = fopen (fileName, "w");
  210.     if (fileP == NULL)
  211.         return TCL_ERROR;
  212.  
  213.     for (memScanP = allocHead; memScanP != NULL; memScanP = memScanP->flink) {
  214.         address = &memScanP->body [0];
  215.         fprintf (fileP, "%8lx - %8lx  %7d @ %s %d", address,
  216.                  address + memScanP->length - 1, memScanP->length,
  217.                  memScanP->file, memScanP->line);
  218.     (void) fputc('\n', fileP);
  219.     }
  220.     fclose (fileP);
  221.     return TCL_OK;
  222. }
  223.  
  224. /*
  225.  *----------------------------------------------------------------------
  226.  *
  227.  * Tcl_DbCkalloc - debugging ckalloc
  228.  *
  229.  *        Allocate the requested amount of space plus some extra for
  230.  *        guard bands at both ends of the request, plus a size, panicing 
  231.  *        if there isn't enough space, then write in the guard bands
  232.  *        and return the address of the space in the middle that the
  233.  *        user asked for.
  234.  *
  235.  *        The second and third arguments are file and line, these contain
  236.  *        the filename and line number corresponding to the caller.
  237.  *        These are sent by the ckalloc macro; it uses the preprocessor
  238.  *        autodefines __FILE__ and __LINE__.
  239.  *
  240.  *----------------------------------------------------------------------
  241.  */
  242. char *
  243. Tcl_DbCkalloc(size, file, line)
  244.     unsigned int size;
  245.     char        *file;
  246.     int          line;
  247. {
  248.     struct mem_header *result;
  249.  
  250.     if (validate_memory)
  251.         Tcl_ValidateAllMemory (file, line);
  252.  
  253.     result = (struct mem_header *)malloc((unsigned)size + 
  254.                               sizeof(struct mem_header) + GUARD_SIZE);
  255.     if (result == NULL) {
  256.         fflush(stdout);
  257.         dump_memory_info(stderr);
  258.         panic("unable to alloc %d bytes, %s line %d", size, file, 
  259.               line);
  260.     }
  261.  
  262.     /*
  263.      * Fill in guard zones and size.  Also initialize the contents of
  264.      * the block with bogus bytes to detect uses of initialized data.
  265.      * Link into allocated list.
  266.      */
  267.     result->length = size;
  268.     result->file = file;
  269.     result->line = line;
  270.     memset ((char *) result->low_guard, GUARD_VALUE, GUARD_SIZE);
  271.     memset (result->body, GUARD_VALUE, size);
  272.     memset (result->body + size, GUARD_VALUE, GUARD_SIZE);
  273.     result->flink = allocHead;
  274.     result->blink = NULL;
  275.     if (allocHead != NULL)
  276.         allocHead->blink = result;
  277.     allocHead = result;
  278.  
  279.     total_mallocs++;
  280.     if (trace_on_at_malloc && (total_mallocs >= trace_on_at_malloc)) {
  281.         (void) fflush(stdout);
  282.         fprintf(stderr, "reached malloc trace enable point (%d)\n",
  283.                 total_mallocs);
  284.         fflush(stderr);
  285.         alloc_tracing = TRUE;
  286.         trace_on_at_malloc = 0;
  287.     }
  288.  
  289.     if (alloc_tracing)
  290.         fprintf(stderr,"ckalloc %lx %d %s %d\n", result->body, size, 
  291.                 file, line);
  292.  
  293.     if (break_on_malloc && (total_mallocs >= break_on_malloc)) {
  294.         break_on_malloc = 0;
  295.         (void) fflush(stdout);
  296.         fprintf(stderr,"reached malloc break limit (%d)\n", 
  297.                 total_mallocs);
  298.         fprintf(stderr, "program will now enter C debugger\n");
  299.         (void) fflush(stderr);
  300.     abort();
  301.     }
  302.  
  303.     current_malloc_packets++;
  304.     if (current_malloc_packets > maximum_malloc_packets)
  305.         maximum_malloc_packets = current_malloc_packets;
  306.     current_bytes_malloced += size;
  307.     if (current_bytes_malloced > maximum_bytes_malloced)
  308.         maximum_bytes_malloced = current_bytes_malloced;
  309.  
  310.     if (init_malloced_bodies)
  311.         memset (result->body, 0xff, (int) size);
  312.  
  313.     return result->body;
  314. }
  315.  
  316. /*
  317.  *----------------------------------------------------------------------
  318.  *
  319.  * Tcl_DbCkfree - debugging ckfree
  320.  *
  321.  *        Verify that the low and high guards are intact, and if so
  322.  *        then free the buffer else panic.
  323.  *
  324.  *        The guards are erased after being checked to catch duplicate
  325.  *        frees.
  326.  *
  327.  *        The second and third arguments are file and line, these contain
  328.  *        the filename and line number corresponding to the caller.
  329.  *        These are sent by the ckfree macro; it uses the preprocessor
  330.  *        autodefines __FILE__ and __LINE__.
  331.  *
  332.  *----------------------------------------------------------------------
  333.  */
  334.  
  335. int
  336. Tcl_DbCkfree(ptr, file, line)
  337.     char *  ptr;
  338.     char     *file;
  339.     int       line;
  340. {
  341.     struct mem_header *memp = 0;  /* Must be zero for size calc */
  342.  
  343.     /*
  344.      * Since header ptr is zero, body offset will be size
  345.      */
  346. #ifdef _CRAYCOM
  347.     memp = (struct mem_header *)((char *) ptr  - (sizeof(int)*((unsigned)&(memp->body))));
  348. #else
  349.     memp = (struct mem_header *)(((char *) ptr) - (int)memp->body);
  350. #endif
  351.  
  352.     if (alloc_tracing)
  353.         fprintf(stderr, "ckfree %lx %ld %s %d\n", memp->body, 
  354.                 memp->length, file, line);
  355.  
  356.     if (validate_memory)
  357.         Tcl_ValidateAllMemory (file, line);
  358.  
  359.     ValidateMemory (memp, file, line, TRUE);
  360.  
  361.     total_frees++;
  362.     current_malloc_packets--;
  363.     current_bytes_malloced -= memp->length;
  364.  
  365.     /*
  366.      * Delink from allocated list
  367.      */
  368.     if (memp->flink != NULL)
  369.         memp->flink->blink = memp->blink;
  370.     if (memp->blink != NULL)
  371.         memp->blink->flink = memp->flink;
  372.     if (allocHead == memp)
  373.         allocHead = memp->flink;
  374.     free((char *) memp);
  375.     return 0;
  376. }
  377.  
  378. /*
  379.  *--------------------------------------------------------------------
  380.  *
  381.  * Tcl_DbCkrealloc - debugging ckrealloc
  382.  *
  383.  *    Reallocate a chunk of memory by allocating a new one of the
  384.  *    right size, copying the old data to the new location, and then
  385.  *    freeing the old memory space, using all the memory checking
  386.  *    features of this package.
  387.  *
  388.  *--------------------------------------------------------------------
  389.  */
  390. char *
  391. Tcl_DbCkrealloc(ptr, size, file, line)
  392.     char *ptr;
  393.     unsigned int size;
  394.     char *file;
  395.     int line;
  396. {
  397.     char *new;
  398.  
  399.     new = Tcl_DbCkalloc(size, file, line);
  400.     memcpy((VOID *) new, (VOID *) ptr, (int) size);
  401.     Tcl_DbCkfree(ptr, file, line);
  402.     return(new);
  403. }
  404.  
  405. /*
  406.  *----------------------------------------------------------------------
  407.  *
  408.  * MemoryCmd --
  409.  *     Implements the TCL memory command:
  410.  *       memory info
  411.  *       memory display
  412.  *       break_on_malloc count
  413.  *       trace_on_at_malloc count
  414.  *       trace on|off
  415.  *       validate on|off
  416.  *
  417.  * Results:
  418.  *     Standard TCL results.
  419.  *
  420.  *----------------------------------------------------------------------
  421.  */
  422.     /* ARGSUSED */
  423. static int
  424. MemoryCmd (clientData, interp, argc, argv)
  425.     char       *clientData;
  426.     Tcl_Interp *interp;
  427.     int         argc;
  428.     char      **argv;
  429. {
  430.     char *fileName;
  431.     Tcl_DString buffer;
  432.     int result;
  433.  
  434.     if (argc < 2) {
  435.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  436.         argv[0], " option [args..]\"", (char *) NULL);
  437.     return TCL_ERROR;
  438.     }
  439.  
  440.     if (strcmp(argv[1],"trace") == 0) {
  441.         if (argc != 3) 
  442.             goto bad_suboption;
  443.         alloc_tracing = (strcmp(argv[2],"on") == 0);
  444.         return TCL_OK;
  445.     }
  446.     if (strcmp(argv[1],"init") == 0) {
  447.         if (argc != 3)
  448.             goto bad_suboption;
  449.         init_malloced_bodies = (strcmp(argv[2],"on") == 0);
  450.         return TCL_OK;
  451.     }
  452.     if (strcmp(argv[1],"validate") == 0) {
  453.         if (argc != 3)
  454.              goto bad_suboption;
  455.         validate_memory = (strcmp(argv[2],"on") == 0);
  456.         return TCL_OK;
  457.     }
  458.     if (strcmp(argv[1],"trace_on_at_malloc") == 0) {
  459.         if (argc != 3) 
  460.             goto argError;
  461.         if (Tcl_GetInt(interp, argv[2], &trace_on_at_malloc) != TCL_OK)
  462.                 return TCL_ERROR;
  463.          return TCL_OK;
  464.     }
  465.     if (strcmp(argv[1],"break_on_malloc") == 0) {
  466.         if (argc != 3) 
  467.             goto argError;
  468.         if (Tcl_GetInt(interp, argv[2], &break_on_malloc) != TCL_OK)
  469.                 return TCL_ERROR;
  470.         return TCL_OK;
  471.     }
  472.  
  473.     if (strcmp(argv[1],"info") == 0) {
  474.         dump_memory_info(stdout);
  475.         return TCL_OK;
  476.     }
  477.     if (strcmp(argv[1],"active") == 0) {
  478.         if (argc != 3) {
  479.         Tcl_AppendResult(interp, "wrong # args:  should be \"",
  480.             argv[0], " active file", (char *) NULL);
  481.         return TCL_ERROR;
  482.     }
  483.     fileName = Tcl_TildeSubst(interp, argv[2], &buffer);
  484.     if (fileName == NULL) {
  485.         return TCL_ERROR;
  486.     }
  487.     result = Tcl_DumpActiveMemory (fileName);
  488.     Tcl_DStringFree(&buffer);
  489.     if (result != TCL_OK) {
  490.         Tcl_AppendResult(interp, "error accessing ", argv[2], 
  491.             (char *) NULL);
  492.         return TCL_ERROR;
  493.     }
  494.     return TCL_OK;
  495.     }
  496.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  497.         "\":  should be info, init, active, break_on_malloc, ",
  498.         "trace_on_at_malloc, trace, or validate", (char *) NULL);
  499.     return TCL_ERROR;
  500.  
  501. argError:
  502.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  503.         " ", argv[1], "count\"", (char *) NULL);
  504.     return TCL_ERROR;
  505.  
  506. bad_suboption:
  507.     Tcl_AppendResult(interp, "wrong # args:  should be \"", argv[0],
  508.         " ", argv[1], " on|off\"", (char *) NULL);
  509.     return TCL_ERROR;
  510. }
  511.  
  512. /*
  513.  *----------------------------------------------------------------------
  514.  *
  515.  * Tcl_InitMemory --
  516.  *     Initialize the memory command.
  517.  *
  518.  *----------------------------------------------------------------------
  519.  */
  520. void
  521. Tcl_InitMemory(interp)
  522.     Tcl_Interp *interp;
  523. {
  524. Tcl_CreateCommand (interp, "memory", MemoryCmd, (ClientData)NULL, 
  525.                   (void (*)())NULL);
  526. }
  527.  
  528. #else
  529.  
  530.  
  531. /*
  532.  *----------------------------------------------------------------------
  533.  *
  534.  * Tcl_Ckalloc --
  535.  *     Interface to malloc when TCL_MEM_DEBUG is disabled.  It does check
  536.  *     that memory was actually allocated.
  537.  *
  538.  *----------------------------------------------------------------------
  539.  */
  540. VOID *
  541. Tcl_Ckalloc (size)
  542.     unsigned int size;
  543. {
  544.         char *result;
  545.  
  546.         result = malloc(size);
  547.         if (result == NULL) 
  548.                 panic("unable to alloc %d bytes", size);
  549.         return result;
  550. }
  551.  
  552. /*
  553.  *----------------------------------------------------------------------
  554.  *
  555.  * TckCkfree --
  556.  *     Interface to free when TCL_MEM_DEBUG is disabled.  Done here rather
  557.  *     in the macro to keep some modules from being compiled with 
  558.  *     TCL_MEM_DEBUG enabled and some with it disabled.
  559.  *
  560.  *----------------------------------------------------------------------
  561.  */
  562. void
  563. Tcl_Ckfree (ptr)
  564.     VOID *ptr;
  565. {
  566.         free (ptr);
  567. }
  568.  
  569. /*
  570.  *----------------------------------------------------------------------
  571.  *
  572.  * Tcl_InitMemory --
  573.  *     Dummy initialization for memory command, which is only available 
  574.  *     if TCL_MEM_DEBUG is on.
  575.  *
  576.  *----------------------------------------------------------------------
  577.  */
  578.     /* ARGSUSED */
  579. void
  580. Tcl_InitMemory(interp)
  581.     Tcl_Interp *interp;
  582. {
  583. }
  584.  
  585. #endif
  586.